package 自己写的

import (
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"strconv"
)

/*
使用http.Head()方法获取文件的大小，然后拆分成多个线程进行下载。
每个线程的起始位置和结束位置计算公式为：bg = i * (size/concurrency)，ed = bg + (size/concurrency) - 1。
我们使用http.NewRequest()方法创建http请求，设置请求头部信息，以获取需要下载的数据块，
然后通过io.Copy()方法将数据写入到文件中，实现快速和高效的文件下载。
*/
func DownloadFile(url, fileName string) error {
	out, err := os.Create(fileName)
	if err != nil {
		return err
	}
	defer out.Close()

	resp, err := http.Head(url)
	if err != nil {
		return err
	}

	size, err := strconv.Atoi(resp.Header.Get("Content-Length"))
	if err != nil {
		return err
	}

	concurrency := 10 // 并发数

	var bg int64 // 起始位置
	var ed int64 // 结束位置
	for i := 0; i < concurrency; i++ {
		bg = int64(i) * int64(size/concurrency)
		ed = bg + int64(size/concurrency) - 1

		go func(idx int, bg, ed int64) {
			req, _ := http.NewRequest(http.MethodGet, url, nil)
			req.Header.Set("Range", fmt.Sprintf("bytes=%v-%v", bg, ed))

			client := &http.Client{}
			resp, err := client.Do(req)
			if err != nil {
				panic(err)
			}
			defer resp.Body.Close()

			out.Seek(bg, 0)
			_, err = io.Copy(out, resp.Body)
			if err != nil {
				panic(err)
			}

			log.Printf("[%d] Done.", idx)
		}(i, bg, ed)
	}

	return nil
}
